#ifdef __GNUG__
#pragma implementation
#endif

#include "SunFont.h"
#include "SunBitmap.h"
#include "System.h"
#include "String.h"
#include "Error.h"
#include "Math.h"
#include "Directory.h"

#include "pix.h"

#define VFontExt "vf"
#define MAXCHAR 256

static char *SunFontFName[] = {
	"times",
	"helvet",
	"courie",
	"symbol",
	"chicag",
	"avgard",
	"bookmn",
	"ceslbk",
	"nhelve",
	"palati",
	"zchanc",
	"zdingb",
	0
};

//---- SunFont -----------------------------------------------------------------

int scale(int x, double s)
{
	return (int) ((double)x*s+0.5);
}

SunFont::SunFont(FontFamily *ff, int ps, GrFace fc, pixfont *p, SunFont *bf)
															: Font(ff, ps, fc)
{
	register pixchar *pc;
	register int i, w, h;
		
	notdef= 0;
	pf= p;
	basefont= bf;

	if (pf) {
		register int maxh= 0, maxb= 0;
		pixchar nd= pf->pf_char['0'];
		w= nd.pc_pr->pr_size.x;
		h= nd.pc_pr->pr_size.y;
		notdef= nd.pc_pr= mem_create(w, h, 1);
		pr_vector(notdef, 1,   1,   w-2, 1,   PIX_OR, 1);
		pr_vector(notdef, w-2, 1,   w-2, h-2, PIX_OR, 1);
		pr_vector(notdef, w-2, h-2, 1,   h-2, PIX_OR, 1);
		pr_vector(notdef, 1,   h-2, 1,   1,   PIX_OR, 1);
		
		for (pc= pf->pf_char, i= 0; i < MAXCHAR; i++, pc++) {
			if (pc->pc_pr) {
				maxh= Math::Max(maxh, -pc->pc_home.y);
				maxb= Math::Max(maxb, pc->pc_pr->pr_size.y + pc->pc_home.y);
			} else
				*pc= nd;
			cw[i]= pc->pc_adv.x;
		}
		ht= maxh;
		bs= maxb;
		ils= maxh + maxb + 3;
	}
	if (basefont && (size != basefont->Size())) {
		double s= (double) size / (double) basefont->Size();
		
		pf= new pixfont;
		*pf= *basefont->pf; // copy all
		
		ht= scale(basefont->ht, s);
		bs= scale(basefont->bs, s);
		ils= scale(basefont->ils, s);
		
		w= scale(basefont->notdef->pr_size.x, s);
		h= scale(basefont->notdef->pr_size.y, s);
		notdef= mem_create(w, h, 1);
		pr_vector(notdef, 1,   1,   w-2, 1,   PIX_OR, 1);
		pr_vector(notdef, w-2, 1,   w-2, h-2, PIX_OR, 1);
		pr_vector(notdef, w-2, h-2, 1,   h-2, PIX_OR, 1);
		pr_vector(notdef, 1,   h-2, 1,   1,   PIX_OR, 1);
		
		for (pc= pf->pf_char, i= 0; i < MAXCHAR; i++, pc++) {
			if (pc->pc_pr == basefont->notdef)
				pc->pc_pr= notdef;
			else
				pc->pc_pr= 0;
			cw[i]= pc->pc_adv.x= scale(pc->pc_adv.x, s);
			pc->pc_home.x= scale(pc->pc_home.x, s);
			pc->pc_home.y= scale(pc->pc_home.y, s);
		}
	}
	if (basefont && (face != basefont->Face())) {
		bool fixed= (Fid() == eFontCourier);
		
		pf= new pixfont;
		*pf= *basefont->pf;   // copy all

		ht= basefont->ht;
		bs= basefont->bs;
		ils= basefont->ils;

		struct pixrect *pr= basefont->pf->pf_char['0'].pc_pr;
		notdef= mem_create(pr->pr_size.x, pr->pr_size.y, 1);
		pr_rop(notdef, 0, 0, pr->pr_size.x, pr->pr_size.y, PIX_SRC, pr, 0, 0);
		
		for (pc= pf->pf_char, i= 0; i < MAXCHAR; i++, pc++) {
			if (pc->pc_pr == basefont->notdef) {
				pc->pc_pr= notdef;
			} else {
				switch (fc ^ basefont->Face()) {
				case eFaceBold:
				case eFaceItalic:
					if (! fixed)
						pc->pc_adv.x++;
					break;
				case eFaceOutline:
					pc->pc_adv.x+= 2;
					break;
				case eFaceShadow:
					pc->pc_adv.x+= 3;
					break;
				default:
					break;
				}
				pc->pc_pr= 0;
			}
			cw[i]= pc->pc_adv.x;
		}
	}
}

SunFont::~SunFont()
{
	if (notdef) {
		pr_destroy(notdef);
		notdef= 0;
	}
	if (pf) {
		if (basefont) {
			basefont= 0;
			delete pf;
		} else
			pf_close(pf);
		pf= 0;
	}
}

struct pixchar *SunFont::GetPixchar(u_int i)
{
	struct pixchar *npc= &pf->pf_char[i];

	if (npc->pc_pr)
		return npc;

	struct pixchar *pcc= basefont->GetPixchar(i);

	if (size != basefont->Size()) {
		double sc= (double) size / (double) basefont->Size();
		pixrect *pr= pcc->pc_pr;
		npc->pc_pr= ScalePixrect(pr, scale(pr->pr_size.x, sc), scale(pr->pr_size.y, sc));
	}

	if (face != basefont->Face()) {
		register int x, y;
		int w= pcc->pc_pr->pr_size.x;
		int h= pcc->pc_pr->pr_size.y;

		switch (face ^ basefont->Face()) {
		case eFaceBold:
			npc->pc_pr= mem_create(w+1, h, 1);
			pr_rop(npc->pc_pr, 0, 0, w, h, PIX_OR, pcc->pc_pr, 0, 0);
			pr_rop(npc->pc_pr, 1, 0, w, h, PIX_OR, pcc->pc_pr, 0, 0);
			break;

		case eFaceItalic:
			{
				int a= h/3;
				npc->pc_pr= mem_create(w + a, h, 1);
				for (y= 0, x= a; y <= h; y+= 3, x--)
					pr_rop(npc->pc_pr, x, y, w, 3, PIX_OR, pcc->pc_pr, 0, y);
				npc->pc_home.x= -(h+pcc->pc_home.y)/3;
			}
			break;

		case eFaceUnderline:
			w= Math::Max(w, Width(i));
			y= -pcc->pc_home.y + 1;
			h= Math::Max(h, y + 2);
			npc->pc_pr= mem_create(w, h, 1);
			pr_rop(npc->pc_pr, 0, y, w, 1, PIX_SET, 0, 0, 0);
			pr_rop(npc->pc_pr, pcc->pc_home.x-1, y, pcc->pc_pr->pr_size.x, 1, PIX_ERASE, pcc->pc_pr, 0, y);
			pr_rop(npc->pc_pr, pcc->pc_home.x+1, y, pcc->pc_pr->pr_size.x, 1, PIX_ERASE, pcc->pc_pr, 0, y);
			pr_rop(npc->pc_pr, pcc->pc_home.x, 0, pcc->pc_pr->pr_size.x, pcc->pc_pr->pr_size.y, PIX_OR, pcc->pc_pr, 0, 0);
			npc->pc_home.x= 0;
			break;

		case eFaceOutline:
			npc->pc_pr= mem_create(w+2, h+2, 1);
			pr_rop(npc->pc_pr, 0, 0, w, h, PIX_OR, pcc->pc_pr, 0, 0);
			pr_rop(npc->pc_pr, 1, 0, w, h, PIX_OR, pcc->pc_pr, 0, 0);
			pr_rop(npc->pc_pr, 2, 0, w, h, PIX_OR, pcc->pc_pr, 0, 0);
			pr_rop(npc->pc_pr, 0, 1, w+2, h+1, PIX_OR, npc->pc_pr, 0, 0);
			pr_rop(npc->pc_pr, 0, 1, w+2, h+1, PIX_OR, npc->pc_pr, 0, 0);
			pr_rop(npc->pc_pr, 1, 1, w, h, PIX_ERASE, pcc->pc_pr, 0, 0);
			npc->pc_home.x--;
			npc->pc_home.y--;
			break;

		case eFaceShadow:
			{
				SunFont *innerfont= (SunFont*) new_Font(Fid(), Size(),
												(GrFace)(Face() & ~eFaceShadow));
				npc->pc_pr= mem_create(w+3, h+3, 1);
				for (x= 0; x < 4; x++)
					pr_rop(npc->pc_pr, x, 0, w, h, PIX_OR, pcc->pc_pr, 0, 0);
				for (x= 0; x < 3; x++)
					pr_rop(npc->pc_pr, 0, 1, w+3, h+2, PIX_OR, npc->pc_pr, 0, 0);
				pr_rop(npc->pc_pr, 1, 1, w, h, PIX_ERASE, innerfont->GetPixchar(i)->pc_pr, 0, 0);
				npc->pc_home.x--;
				npc->pc_home.y--;
			}
			break;

		default:
			npc->pc_pr= notdef;
			break;
		}
	}
	return &pf->pf_char[i];
}

DevBitmap *SunFont::CharAsBitmap(u_int c, Point *offset)
{
	struct pixchar *pc= GetPixchar(c);

	if (pc->pc_pr && (pc->pc_pr != notdef)) {
		offset->x= pc->pc_home.x;
		offset->y= -pc->pc_home.y;
		return new SunBitmap(pc->pc_pr);
	}
	return 0;
}

//---- SunFontManager ----------------------------------------------------------

char **SunFontManager::VFontTable= 0;

SunFontManager::SunFontManager() : FontManager()
{
	register const char *name;
	register int i= 0;
	Directory dir(form("%s/sunfonts", gEtDir));

	if (VFontTable == 0)
		VFontTable= new char* [200];

	while (name= dir())
		if (strcmp(&name[strlen(name)-3], ".vf") == 0)
			VFontTable[i++]= strsave(name);
}

SunFontManager::~SunFontManager()
{
	for (int i= 0; VFontTable[i]; i++)
		SafeDelete(VFontTable[i]);
	SafeDelete(VFontTable);
}

Font* SunFontManager::MakeFont(FontFamily *ff, int ps, GrFace fc, bool dflt)
{
	pixfont *pf;

	if (ps >= 6 && ps <= 24
			&& ((fc & (eFaceUnderline|eFaceOutline|eFaceShadow)) == 0)) {
		char *name2= form("%s%02d%s.%s",
				SunFontFName[ff->Fid()], ps, gFontManager->StyleString(fc),
																	VFontExt);
		char *name= form("%s/sunfonts/%s", gEtDir, name2);

		if (VFontTable) {
			for (int i= 0; VFontTable[i]; i++) {
				if (strcmp(VFontTable[i], name2) == 0) {
					if (pf= pf_open_private(name))
						return new SunFont(ff, ps, fc, pf, 0);
					*VFontTable[i]= ' ';    // no font; disable entry
				}
			}
				
		} else {
			if (pf= pf_open_private(name))
				return new SunFont(ff, ps, fc, pf, 0);
		}
	}
	if (dflt && (pf= pf_default()))
		return new SunFont(ff, ps, fc, pf, 0);
	return 0;
}

extern bool freal;

Font *SunFontManager::ScaleFont2(FontFamily *ff, int ps, GrFace fc)
{
	Font *f= 0;
	int i, k;

	if (f= ff->MapSizeFace2(ps, fc))
		return f;
	freal= TRUE;
	if (f= ff->MapSizeFace2(ps+1, fc))
		return f;
	freal= TRUE;
	if (f= ff->MapSizeFace2(ps+2, fc))
		return f;
	freal= TRUE;
	if (f= ff->MapSizeFace2(ps-1, fc))
		return f;
	freal= TRUE;
	if (f= ff->MapSizeFace2(ps-2, fc))
		return f;
	for (k= 2; k < 5; k++) {
		freal= TRUE;
		if (f= ff->MapSizeFace2(ps*k, fc))
			return new SunFont(ff, ps, fc, 0, (SunFont*)f);
	}
	for (k= 2; k < 5; k++) {
		freal= TRUE;
		if (f= ff->MapSizeFace2(ps/k, fc))
			return new SunFont(ff, ps, fc, 0, (SunFont*)f);
	}
	for (i= 3; i < 200; i++) {
		freal= TRUE;
		if (f= ff->MapSizeFace2(ps-i, fc))
			return new SunFont(ff, ps, fc, 0, (SunFont*)f);
		freal= TRUE;
		if (f= ff->MapSizeFace2(ps+i, fc))
			return new SunFont(ff, ps, fc, 0, (SunFont*)f);
	}
	return 0;
}

Font *SunFontManager::ScaleFont(FontFamily *ff, int ps, GrFace face)
{
	Font *f= ScaleFont2(ff, ps, face);
	if (f == 0 && face != eFacePlain) {
		GrFace fc= face;
		for (int facebit= eFaceShadow; facebit; facebit >>= 1) {
			if (fc & facebit) {
				fc= (GrFace) (fc & ~facebit);
				break;
			}
		}
		f= ScaleFont2(ff, ps, fc);
		if (f)
			return new SunFont(ff, ps, face, 0, (SunFont*)f);
	}
	return f;
}

